/*
    This file is part of Emu.

    Emu is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Emu is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Emu.  If not, see <http://www.gnu.org/licenses/>. */
#include <QDomDocument>
#include <QFile>

#include "eprofilereader.h"
#include "esessutils.h"

EProfileReader::EProfileReader()
{

}

EProfile *EProfileReader::readPrefs(QString path, QString *err)
{
    int errorColumn, errorLine;
    QFile loadFile(path);
    QDomDocument loadDoc;
    QDomElement element;
    QString errorRet;

    _profile = new EProfile;

    /* There isn't any good error message to show here. Saying that the profile
       is broken doesn't help them fix it. On the other hand, showing them the
       profile path might confuse them.
       Err on the side of caution and give some details. */
    if (!loadFile.open(QFile::ReadOnly)) {
        *err = tr("Can't open profile at %1.").arg(path);
        return _profile;
    }

    if (!loadDoc.setContent(&loadFile, true, &errorRet, &errorLine,
        &errorColumn)) {
        *err = tr("Error in profile at %1 at line %2, column %3 : %4").arg(path)
                        .arg(errorLine).arg(errorColumn).arg(errorRet);

        loadFile.close();
        return _profile;
    }

    loadFile.close();

    _root = loadDoc.documentElement();
    element = _root.firstChildElement("host");
    if (element.isNull()) {
        *err = tr("Profile at %1 does not have host info.").arg(path);
        return _profile;
    }

    _profile->setHostName(element.attribute("name"));
    _profile->setHostAddress(element.attribute("address"));
    _profile->setHostPort(element.attribute("port"));

    QString titleStr;
    titleStr = tr("%1@%2:%3")
            .arg(_profile->hostName())
            .arg(_profile->hostAddress())
            .arg(_profile->hostPort());

    _profile->setTitle(titleStr);
    _profile->setProfilePath(path);
    readAliases();
    readDisplay();
    readFilters();
    readHighlights();
    readMacros();
    readTriggers();

    return _profile;
}

void EProfileReader::readAliases()
{
    QDomElement element = _root.firstChildElement("alias");
    QList <EAlias> aliases;

    while (!element.isNull()) {
        EAlias alias;
        QDomElement inputElem = element.firstChildElement("input");
        QDomElement outputElem = element.firstChildElement("output");

        if (inputElem.isNull() || outputElem.isNull()) {
            element = element.nextSiblingElement("alias");
            continue;
        }

        alias.setInput(inputElem.text());
        alias.setName(element.attribute("name"));
        alias.setOutput(outputElem.text());
        aliases.append(alias);

        element = element.nextSiblingElement("alias");
    }
    _profile->setAliases(aliases);
}


void EProfileReader::readDisplay()
{
    QDomElement element = _root.firstChildElement("display");

    if (!element.isNull()) {
        element = element.firstChildElement("color");
        QStringList colorNames = (QStringList() << "black" << "red" << "green"
         << "yellow" << "blue" << "magenta" << "cyan" << "white" << "boldblack"
         << "boldred" << "boldgreen" << "boldyellow" << "boldblue"
         << "boldmagenta" << "boldcyan" << "boldwhite" << "foreground"
         << "background" << "messageforeground" << "messagebackground");

        while (!element.isNull()) {
            QString name = element.attribute("name");
            QString colorValue = element.attribute("value");
            /* Reject colors without a name, and those not in the format of
               #rrggbb. */
            if (name.isNull() || !colorValue.startsWith("#") ||
                colorValue.size() != 7) {
                element = element.nextSiblingElement("color");
                continue;
            }

            name = name.toLower();

            QColor c;
            c.setNamedColor(colorValue);
            /* Make sure the color is a valid one. */
            if (!c.isValid()) {
                element = element.nextSiblingElement("color");
                continue;
            }

            int where = colorNames.indexOf(name);
            if (where != -1)
                _profile->setColor(where, c);

            element = element.nextSiblingElement("color");
        }
    }
}

void EProfileReader::readFilters()
{
    QDomElement element = _root.firstChildElement("filter");
    QList <ETrigger> triggers;

    /* Old-style filters are scanned in as new-style triggers.
       Only set the omit trigger option. */
    while (!element.isNull()) {
        ETrigger trigger;
        QDomElement syntaxElem = element.firstChildElement("syntax");
        QDomElement inputElem = element.firstChildElement("input");

        if (inputElem.isNull() || syntaxElem.isNull()) {
            element = element.nextSiblingElement("filter");
            continue;
        }

        QString syntax = syntaxElem.text();

        trigger.setPattern(inputElem.text());

        if (syntax == "Regular Expression")
            trigger.setPatternSyntax(QRegExp::RegExp);
        else if (syntax == "Wildcard")
            trigger.setPatternSyntax(QRegExp::Wildcard);
        else if (syntax == "Fixed String")
            trigger.setPatternSyntax(QRegExp::FixedString);
        else {
            element = element.nextSiblingElement("filter");
            continue;
        }

        QString caseSensitivity = element.attribute("case_sensitive");

        if (caseSensitivity == "1")
            trigger.setCaseSensitivity(Qt::CaseSensitive);
        else if (caseSensitivity == "0")
            trigger.setCaseSensitivity(Qt::CaseInsensitive);
        else {
            element = element.nextSiblingElement("filter");
            continue;
        }

        trigger.setName(element.attribute("name"));
        trigger.setOptions(TRIG_OMIT);
        triggers.append(trigger);

        element = element.nextSiblingElement("filter");
    }
    _profile->setTriggers(triggers);
}

void EProfileReader::readHighlights()
{
    QDomElement element = _root.firstChildElement("highlight");
    QList <ETrigger> triggers;

    while (!element.isNull()) {
        ETrigger trigger;
        QDomElement syntaxElem = element.firstChildElement("syntax");
        QDomElement inputElem = element.firstChildElement("input");

        if (inputElem.isNull() || syntaxElem.isNull()) {
            element = element.nextSiblingElement("highlight");
            continue;
        }

        QString syntax = syntaxElem.text();

        trigger.setPattern(inputElem.text());

        if (syntax == "Regular Expression")
            trigger.setPatternSyntax(QRegExp::RegExp);
        else if (syntax == "Wildcard")
            trigger.setPatternSyntax(QRegExp::Wildcard);
        else if (syntax == "Fixed String")
            trigger.setPatternSyntax(QRegExp::FixedString);
        else {
            element = element.nextSiblingElement("highlight");
            continue;
        }

        QString caseSensitivity = element.attribute("case_sensitive");

        if (caseSensitivity == "1")
            trigger.setCaseSensitivity(Qt::CaseSensitive);
        else if (caseSensitivity == "0")
            trigger.setCaseSensitivity(Qt::CaseInsensitive);
        else {
            element = element.nextSiblingElement("highlight");
            continue;
        }

        QString colorValue = element.attribute("color");
        if (colorValue.isNull() || !colorValue.startsWith("#") ||
            colorValue.size() != 7) {
            element = element.nextSiblingElement("highlight");
            continue;
        }

        QColor c;
        c.setNamedColor(colorValue);

        if (!c.isValid()) {
            element = element.nextSiblingElement("highlight");
            continue;
        }

        trigger.setName(element.attribute("name"));
        trigger.setHighlightColor(c);
        trigger.setOptions(TRIG_HIGHLIGHT);
        triggers.append(trigger);

        element = element.nextSiblingElement("highlight");
    }

    _profile->setTriggers(_profile->triggers() + triggers);
}

void EProfileReader::readMacros()
{
    QDomElement element = _root.firstChildElement("macro");
    QList <EMacro> macros;

    while (!element.isNull()) {
        QDomElement inputElem = element.firstChildElement("input");
        QDomElement outputElem = element.firstChildElement("output");
        QDomElement toElem = element.firstChildElement("to");

        if (inputElem.isNull() || outputElem.isNull() || toElem.isNull()) {
            element = element.nextSiblingElement("macro");
            continue;
        }

        int press = ::pressFromText(inputElem.text());

        if (press == 0) {
            element = element.nextSiblingElement("macro");
            continue;
        }

        int to;

        if (toElem.text() == "Input Window")
            to = true;
        else if (toElem.text() == "Server")
            to = false;
        else {
            element = element.nextSiblingElement("macro");
            continue;
        }

        EMacro macro;
        macro.setPress(press);
        macro.setData(outputElem.text());
        macro.setTo(to);
        macro.setName(element.attribute("name"));
        macros.append(macro);

        element = element.nextSiblingElement("macro");
    }

    _profile->setMacros(macros);
}

void EProfileReader::readTriggers()
{
    QDomElement element = _root.firstChildElement("trigger");
    QList <ETrigger> triggers;

    while (!element.isNull()) {
        ETrigger trigger;
        QDomElement syntaxElem = element.firstChildElement("syntax");
        QDomElement inputElem = element.firstChildElement("input");
        QDomElement outputElem = element.firstChildElement("output");
        int options = 0;

        if (inputElem.isNull() || syntaxElem.isNull() || outputElem.isNull()) {
            element = element.nextSiblingElement("trigger");
            continue;
        }

        QString caseSensitivity = element.attribute("case_sensitive");

        if (caseSensitivity == "1")
            trigger.setCaseSensitivity(Qt::CaseSensitive);
        else if (caseSensitivity == "0")
            trigger.setCaseSensitivity(Qt::CaseInsensitive);
        else {
            element = element.nextSiblingElement("trigger");
            continue;
        }

        QString action = element.attribute("action");

        if (action == "omit")
            options |= TRIG_OMIT;
        else if (action == "highlight") {
            QString fgAttr = element.attribute("fg");

            /* Find out what color to highlight with. */
            if (!fgAttr.startsWith("#") || fgAttr.size() != 7) {
                element = element.nextSiblingElement("trigger");
                continue;
            }

            QColor fgColor;
            fgColor.setNamedColor(fgAttr);
            if (!fgColor.isValid()) {
                element = element.nextSiblingElement("trigger");
                continue;
            }

            trigger.setHighlightColor(fgColor);
            options |= TRIG_HIGHLIGHT;
        }
        else if (!action.isEmpty()) {
            element = element.nextSiblingElement("trigger");
            continue;
        }

        QString keepgoing = element.attribute("keepgoing");
        /* Old-style triggers continued after matching. The old-style triggers
           didn't have this attribute, so assume this is wanted if empty.
           This will be removed when the compatibility is removed. */
        if (keepgoing == "1" || keepgoing.isEmpty())
            options |= TRIG_KEEPGOING;
        else if (keepgoing != "0") {
            element = element.nextSiblingElement("trigger");
            continue;
        }

        QString syntax = syntaxElem.text();

        trigger.setPattern(inputElem.text());

        if (syntax == "Regular Expression") {
            trigger.setPatternSyntax(QRegExp::RegExp);
            if (trigger.pattern().count(QRegExp("\\((?!\\?\\!)")))
                options |= TRIG_CAPTURING;
        }
        else if (syntax == "Wildcard")
            trigger.setPatternSyntax(QRegExp::Wildcard);
        else if (syntax == "Fixed String")
            trigger.setPatternSyntax(QRegExp::FixedString);
        else {
            element = element.nextSiblingElement("trigger");
            continue;
        }

        if (!outputElem.text().isEmpty())
            options |= TRIG_HASREPLY;

        trigger.setOptions(options);
        trigger.setName(element.attribute("name"));
        trigger.setOutput(outputElem.text());
        triggers.append(trigger);

        element = element.nextSiblingElement("trigger");
    }
    _profile->setTriggers(triggers);
}
